/*
 * @(#)XFileChooser.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.util;

import javax.swing.*;
import javax.swing.filechooser.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.Vector;
import java.util.Stack;

/**
 * Clase que desarrolla un di�logo para seleccionar un fichero
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XFileChooser extends JDialog
implements ActionListener, MouseListener, WindowListener {

	//----------------------------------------------------------------------------//
	//                           CONSTANTES PRIVADAS                              //
	//----------------------------------------------------------------------------//

	/**
	 * C�digo asociado a la clase serializable
	 */
	private static final long serialVersionUID = 95505666603005L;

	//----------------------------------------------------------------------------//
	//                           CONSTANTES P�BLICAS                              //
	//----------------------------------------------------------------------------//

	/**
	 * C�digo para indicar un di�logo para cargar un fichero
	 */
	public static final int LOAD = 1;
	
	/**
	 * C�digo para indicar un di�logo para salvar un fichero
	 */
	public static final int SAVE = 2;
	
	/**
	 * C�digo para indicar un di�logo para seleccionar un fichero
	 */
	public static final int SELECT = 3;
	
	/**
	 * C�digo para indicar un di�logo para seleccionar o no un fichero
	 */
	public static final int UNSELECT = 4;

	/**
	 * C�digo para indicar que la respuesta ha sido una selecci�n
	 */
	public static final int SELECTED = 1;
	
	/**
	 * C�digo para indicar que la respuesta ha sido una deselecci�n
	 */
	public static final int UNSELECTED = 2;
	
	/**
	 * C�digo para indicar que la respuesta ha sido una cancelaci�n
	 */
	public static final int CANCELED = 3;

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Directorio a mostrar
	 */
	private File currentDirectory;
	
	/**
	 * Filtro de ficheros a utilizar
	 */
	private FileFilter currentFilter;
	
	/**
	 * Tipo de di�logo de selecci�n
	 */
	private int mode = LOAD;
	
	/**
	 * Estado que indica la respuesta dada
	 */
	private int state = 0;

	/**
	 * Campo para introducir el nombre del fichero
	 */
	private XTextField filefield;
	
	/**
	 * Lista para mostrar el contenido del directorio actual
	 */
	private JList list;
	
	/**
	 * Lista desplegable para seleccionar el filtro de ficheros
	 */
	private JComboBox filterCombo;
	
	/**
	 * Lista desplegable para mostrar la ruta hasta el directorio actual
	 */
	private JComboBox directory;
	
	/**
	 * Vista de ficheros proporcionada por el sistema
	 */
	private FileSystemView systemview;

	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public XFileChooser(File file, int mode) {
		super((Frame) null,XConstants.version,true);
		this.systemview = FileSystemView.getFileSystemView();
		this.currentDirectory = initCurrentDirectory(file);
		this.currentFilter = new XFileFilter(XFileFilter.ALLFILES);
		this.mode = mode;
		build();
		setDir();
		setList();
		setFile(file);
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Muestra la ventana de seleccion de ficheros
	 */
	public int showDialog() {
		setVisible(true);
		return state;
	}

	/**
	 * Devuelve el estado de la selecci�n
	 */
	public int getValue() {
		return state;
	}

	/**
	 * Devuelve el fichero seleccionado
	 */
	public File getSelectedFile() {
		if(state != SELECTED) return null;
		String name = filefield.getText();
		if(name.trim().length() == 0) return currentDirectory.getAbsoluteFile();
		return new File(currentDirectory,name);
	}

	/**
	 * A�ade un filtro de ficheros
	 */
	public void addFileFilter(FileFilter filter) {
		currentFilter = filter;
		filterCombo.addItem(filter);
		filterCombo.setSelectedItem(filter);
		setList();
	}

	/**
	 * A�ade un filtro para seleccionar solo directorios
	 */
	public void addNoneFilter() {
		currentFilter = new XFileFilter(XFileFilter.NONEFILE);
		filterCombo.addItem(currentFilter);
		filterCombo.setSelectedItem(currentFilter);
		setList();
	}

	/**
	 * Crea y a�ade un filtro para ficheros con una extensi�n
	 */
	public void addFileFilter(String ending, String description) {
		FileFilter filter = new XFileFilter(ending,description);
		currentFilter = filter;
		filterCombo.addItem(filter);
		filterCombo.setSelectedItem(filter);
		setList();
	}

	/**
	 * Crea y a�ade un filtro para ficheros con una extensi�n
	 */
	public void addFileFilter(Vector<String> endings, String description) {
		FileFilter filter = new XFileFilter(endings,description);
		currentFilter = filter;
		filterCombo.addItem(filter);
		filterCombo.setSelectedItem(filter);
		setList();
	}
	 
	/**
	 * Obtiene el icono correspondiente a un fichero
	 */
	public Icon getIcon(File file) {
		if(file == null) return XFileIcons.doc;
		if(systemview.isRoot(file)) return XFileIcons.hdisk;
		if(file == currentDirectory) return XFileIcons.ofolder;
		if(file.isDirectory()) return XFileIcons.folder;
		if(file.getName().endsWith(".xfl")) return XFileIcons.xfuzzy;
		if(file.getName().endsWith(".pkg")) return XFileIcons.pkg;
		if(file.getName().endsWith(".cfg")) return XFileIcons.xfconfig;
		return XFileIcons.doc;
	}

	/**
	 * Interfaz ActionListener
	 */
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		if(command.equals("Cancel")) actionCancel();
		else if(command.equals("Up")) actionUp();
		else if(command.equals("Home")) actionHome();
		else if(command.equals("Select")) actionSelect();
		else if(command.equals("Unselect")) actionUnselect();
		else if(command.equals("ChangeDir")) actionDir();
		else if(command.equals("ChangeFilter")) actionFilter();
	}

	/**
	 * Interfaz MouseListener. Acci�n al pulsar el rat�n
	 */
	public void mouseClicked(MouseEvent e) {
		actionClick(e);
	}

	/**
	 * Interfaz MouseListener. Acci�n al apretar un bot�n del rat�n
	 */
	public void mousePressed(MouseEvent e) {
	}

	/**
	 * Interfaz MouseListener. Acci�n al soltar un bot�n del rat�n
	 */
	public void mouseReleased(MouseEvent e) {
	}

	/**
	 * Interfaz MouseListener. Acci�n al entrar en la ventana
	 */
	public void mouseEntered(MouseEvent e) {
	}

	/**
	 * Interfaz MouseListener. Acci�n al salir de la ventana
	 */
	public void mouseExited(MouseEvent e) {
	}

	/**
	 * Interfaz WindowListener. Acci�n al abrir la ventana
	 */
	public void windowOpened(WindowEvent e) {
	}

	/**
	 * Interfaz WindowListener. Acci�n al cerrar la ventana
	 */
	public void windowClosing(WindowEvent e) {
		actionCancel();
	}

	/**
	 * Interfaz WindowListener. Acci�n al finalizar el cierre
	 */
	public void windowClosed(WindowEvent e) {
	}

	/**
	 * Interfaz WindowListener. Acci�n al minimizar la ventana
	 */
	public void windowIconified(WindowEvent e) {
	}

	/**
	 * Interfaz WindowListener. Acci�n al maximizar la ventana
	 */
	public void windowDeiconified(WindowEvent e) {
	}

	/**
	 * Interfaz WindowListener. Acci�n al activar la ventana
	 */
	public void windowActivated(WindowEvent e) {
	}
	
	/**
	 * Interfaz WindowListener. Acci�n al desactivar la ventana
	 */
	public void windowDeactivated(WindowEvent e) {
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Construye la ventana
	 */
	private void build() {
		Container content = getContentPane();
		content.setLayout(new BoxLayout(content,BoxLayout.Y_AXIS));
		content.add(upform());
		content.add(listform());
		if(mode == UNSELECT) content.add(downform2());
		else content.add(downform1());
		pack();
		setLocation();
		addWindowListener(this);
	}

	/**
	 * Construye el panel superior de la ventana
	 */
	private Component upform() {
		JLabel label = new JLabel("Look in: ");
		label.setForeground(Color.black);

		directory = new JComboBox();
		directory.setRenderer(new XFileRenderer(XFileRenderer.DIRECTORY, this));
		directory.setActionCommand("ChangeDir");
		directory.addActionListener(this);
		directory.setFont(XConstants.textfont);

		JButton up = new JButton(XFileIcons.upfolder);
		up.addActionListener(this);
		up.setActionCommand("Up");
		up.setBorder(BorderFactory.createRaisedBevelBorder());
		up.setAlignmentY(0.5f);
		JButton home = new JButton(XFileIcons.home);
		home.addActionListener(this);
		home.setActionCommand("Home");
		home.setBorder(BorderFactory.createRaisedBevelBorder());
		home.setAlignmentY(0.5f);

		Box form = new Box(BoxLayout.X_AXIS);
		form.add(Box.createHorizontalStrut(10));
		form.add(label);
		form.add(Box.createHorizontalStrut(10));
		form.add(directory);
		form.add(Box.createHorizontalStrut(10));
		form.add(up);
		form.add(Box.createHorizontalStrut(10));
		form.add(home);
		form.add(Box.createHorizontalStrut(10));

		JPanel upform = new JPanel();
		upform.setLayout(new BoxLayout(upform,BoxLayout.Y_AXIS));
		upform.add(Box.createVerticalStrut(5));
		upform.add(form);
		upform.add(Box.createVerticalStrut(5));

		Dimension minsize = upform.getMinimumSize();
		Dimension prfsize = upform.getPreferredSize();
		Dimension maxsize = upform.getMaximumSize();
		upform.setMinimumSize(new Dimension(minsize.width,prfsize.height));
		upform.setMaximumSize(new Dimension(maxsize.width,prfsize.height));
		return upform;
	}

	/**
	 * Construye el panel central de la ventana	
	 */
	private Component listform() {
		list = new JList();
		list.setBorder(BorderFactory.createLoweredBevelBorder());
		list.setBackground(XConstants.textbackground);
		list.setSelectionBackground(XConstants.textselectedbg);
		list.addMouseListener(this);
		list.setCellRenderer(new XFileRenderer(XFileRenderer.FILE,this));
		return new JScrollPane(list);
	}

	/**
	 * Construye el panel inferior de la ventana (con 2 botones)
	 */
	private Component downform1() {
		String selectlabel;
		switch(mode) {
			case LOAD: selectlabel = "Load"; break;
			case SAVE: selectlabel = "Save"; break;
			default: selectlabel = "Select"; break;
		}

		JLabel label1 = new JLabel("File name: ");
		JLabel label2 = new JLabel("Files of type: ");
		label1.setForeground(Color.black);
		label2.setForeground(Color.black);
		Dimension prefsize = label1.getPreferredSize();
		Dimension maxsize = label1.getMaximumSize();
		prefsize.width = 120;
		maxsize.width = 120;
		label1.setPreferredSize(prefsize);
		label2.setPreferredSize(prefsize);
		label1.setMaximumSize(maxsize);
		label2.setMaximumSize(maxsize);

		filefield = new XTextField("",200);
		filefield.pack();
		filterCombo = new JComboBox();
		filterCombo.setRenderer(new XFileRenderer(XFileRenderer.FILTER,this));
		filterCombo.addItem(currentFilter);
		filterCombo.setActionCommand("ChangeFilter");
		filterCombo.addActionListener(this);
		filterCombo.setFont(XConstants.textfont);
		XButton select = new XButton(selectlabel, "Select", this, 100);
		XButton cancel = new XButton("Cancel", "Cancel", this, 100);
		select.setMaximumWidth(100);
		cancel.setMaximumWidth(100);

		Box form1 = new Box(BoxLayout.X_AXIS);
		form1.add(Box.createHorizontalStrut(10));
		form1.add(label1);
		form1.add(Box.createHorizontalStrut(10));
		form1.add(filefield);
		form1.add(Box.createHorizontalStrut(10));
		form1.add(select);
		form1.add(Box.createHorizontalStrut(10));

		Box form2 = new Box(BoxLayout.X_AXIS);
		form2.add(Box.createHorizontalStrut(10));
		form2.add(label2);
		form2.add(Box.createHorizontalStrut(10));
		form2.add(filterCombo);
		form2.add(Box.createHorizontalStrut(10));
		form2.add(cancel);
		form2.add(Box.createHorizontalStrut(10));

		JPanel downform = new JPanel();
		downform.setLayout(new BoxLayout(downform,BoxLayout.Y_AXIS));
		downform.add(Box.createVerticalStrut(5));
		downform.add(form1);
		downform.add(Box.createVerticalStrut(5));
		downform.add(form2);
		downform.add(Box.createVerticalStrut(5));

		Dimension minsize = downform.getMinimumSize();
		Dimension prfsize = downform.getPreferredSize();
		maxsize = downform.getMaximumSize();
		downform.setMinimumSize(new Dimension(minsize.width,prfsize.height));
		downform.setMaximumSize(new Dimension(maxsize.width,prfsize.height));
		return downform;
	}

	/**
	 * Construye el panel inferior de la ventana (con 3 botones)
	 */
	private Component downform2() {
		JLabel label1 = new JLabel("File name: ");
		JLabel label2 = new JLabel("Files of type: ");
		label1.setForeground(Color.black);
		label2.setForeground(Color.black);
		Dimension prefsize = label1.getPreferredSize();
		Dimension maxsize = label1.getMaximumSize();
		prefsize.width = 120;
		maxsize.width = 120;
		label1.setPreferredSize(prefsize);
		label2.setPreferredSize(prefsize);
		label1.setMaximumSize(maxsize);
		label2.setMaximumSize(maxsize);

		filefield = new XTextField("",200);
		filefield.pack();
		filterCombo = new JComboBox();
		filterCombo.setRenderer(new XFileRenderer(XFileRenderer.FILTER,this));
		filterCombo.addItem(currentFilter);
		filterCombo.setActionCommand("ChangeFilter");
		filterCombo.addActionListener(this);
		XButton select = new XButton("Select", "Select", this);
		XButton unselect = new XButton("Unselect", "Unselect", this);
		XButton cancel = new XButton("Cancel", "Cancel", this);
		select.setMaximumWidth(100);
		unselect.setMaximumWidth(100);
		cancel.setMaximumWidth(100);

		Box form1 = new Box(BoxLayout.X_AXIS);
		form1.add(Box.createHorizontalStrut(10));
		form1.add(label1);
		form1.add(Box.createHorizontalStrut(10));
		form1.add(filefield);
		form1.add(Box.createHorizontalStrut(10));

		Box form2 = new Box(BoxLayout.X_AXIS);
		form2.add(Box.createHorizontalStrut(10));
		form2.add(label2);
		form2.add(Box.createHorizontalStrut(10));
		form2.add(filterCombo);
		form2.add(Box.createHorizontalStrut(10));

		Box form3 = new Box(BoxLayout.Y_AXIS);
		form3.add(Box.createVerticalStrut(5));
		form3.add(select);
		form3.add(Box.createVerticalStrut(5));
		form3.add(unselect);
		form3.add(Box.createVerticalStrut(5));
		form3.add(cancel);
		form3.add(Box.createVerticalStrut(5));

		Box vform = new Box(BoxLayout.Y_AXIS);
		vform.add(Box.createVerticalStrut(5));
		vform.add(form1);
		vform.add(Box.createVerticalStrut(5));
		vform.add(form2);
		vform.add(Box.createVerticalStrut(5));

		JPanel downform = new JPanel();
		downform.setLayout(new BoxLayout(downform,BoxLayout.X_AXIS));
		downform.add(vform);
		downform.add(form3);

		Dimension minsize = downform.getMinimumSize();
		Dimension prfsize = downform.getPreferredSize();
		maxsize = downform.getMaximumSize();
		downform.setMinimumSize(new Dimension(minsize.width,prfsize.height));
		downform.setMaximumSize(new Dimension(maxsize.width,prfsize.height));
		return downform;
	}

	/**
	 * Coloca la ventana en el centro de la pantalla
	 */
	private void setLocation(){
		Dimension frame = getSize();
		Dimension screen = getToolkit().getScreenSize();
		setLocation((screen.width - frame.width)/2,(screen.height - frame.height)/2);
	}

	/**
	 * Utiliza el fichero para tomar el directorio de partida
	 */
	private File initCurrentDirectory(File file) {
		File dir = null;
		if(file == null) dir = new File(System.getProperty("user.dir"));
		else if(file.isDirectory()) dir = file.getAbsoluteFile();
		else dir = file.getAbsoluteFile().getParentFile();
		if(!dir.exists()) dir = new File(System.getProperty("user.dir"));
		return dir;
	}

	/**
	 * Actualiza la lista desplegable de directorios
	 */
	private void setDir() {
		boolean added = false;
		if(directory.getItemCount()>0) directory.removeAllItems();
		Stack<File> dirlist = new Stack<File>();
		File current = currentDirectory;
		File f = currentDirectory;
		while(f != null) { dirlist.push(f); f = f.getParentFile(); }
		File frt = (File) dirlist.pop();
		File roots[] = systemview.getRoots();
		for(int i = 0; i<roots.length; i++) {
			if(roots[i].equals(frt)) {
				directory.addItem(frt);
				while(!dirlist.empty()) directory.addItem(dirlist.pop());
				added = true;
			} else { directory.addItem(roots[i]); }
		}
		if(!added) while(!dirlist.empty()) directory.addItem(dirlist.pop());
		currentDirectory = current;
		directory.setSelectedItem(currentDirectory);
	}

	/**
	 * Actualiza la lista de ficheros del directorio seleccionado
	 */
	private void setList() {
		Vector<File> filelist = new Vector<File>();
		String filename[] = currentDirectory.list();
		for(int i=0; i<filename.length; i++) {
			File file = new File(currentDirectory,filename[i]);
			if(file.isDirectory() || currentFilter.accept(file))
				filelist.addElement(file);
		}
		list.setListData(filelist);
	}

	/**
	 * Selecciona un determinado fichero de la lista
	 */
	private void setFile(File filesel) {
		if(filesel == null || filesel.isDirectory()) return;
		filefield.setText(filesel.getName());
		int index = -1;
		String filename[] = currentDirectory.list();
		for(int i=0; i<filename.length; i++) {
			File file = new File(currentDirectory,filename[i]);
			if(file.isDirectory() || currentFilter.accept(file)) index++;
			if(filename[i].equals(filesel.getName())) break;
		}
		if(index>=0) list.setSelectedIndex(index);
	}

	/**
	 * Acci�n a realizar al picar sobre un fichero	de la lista
	 */
	private void actionClick(MouseEvent e) {
		File file = (File) list.getSelectedValue();
		if(file == null) return;
		if(e.getClickCount() == 1) filefield.setText(file.getName());
		if(e.getClickCount() == 2) {
			if(file.isDirectory()) {
				currentDirectory = file;
				filefield.setText("");
				setDir();
				setList();
			}
			else {
				filefield.setText(file.getName());
				state = SELECTED;
				setVisible(false);
			}
		}
	}

	/**
	 * Acci�n al cambiar de directorio en el men� desplegable
	 */
	private void actionDir() {
		File dir = (File) directory.getSelectedItem();
		if(dir == null) return;
		if(currentDirectory == dir) return;
		currentDirectory = dir;
		setDir();
		setList();
	}

	/**
	 * Acci�n de subir en el arbol de directorios
	 */
	private void actionUp() {
		File upfolder = currentDirectory.getParentFile();
		if(upfolder == null) return;
		currentDirectory = upfolder.getAbsoluteFile();
		setDir();
		setList();
	}

	/**
	 * Acci�n de desplazarse al directorio personal del usuario
	 */
	private void actionHome() {
		String home = System.getProperty("user.home");
		currentDirectory = new File(home);
		setDir();
		setList();
	}

	/**
	 * Acci�n al modificar el filtro de ficheros
	 */
	private void actionFilter() {
		FileFilter filter = (FileFilter) filterCombo.getSelectedItem();
		if(filter == null) return;
		currentFilter = filter;
		setList();
	}

	/**
	 * Acci�n al pulsar el bot�n de selecci�n
	 */
	private void actionSelect() {
		state = SELECTED;
		setVisible(false);
	}

	/**
	 * Acci�n al pulsar el bot�n de deselecci�n
	 */
	private void actionUnselect() {
		state = UNSELECTED;
		setVisible(false);
	}

	/**
	 * Acci�n al pulsar el bot�n de cancelaci�n
	 */
	private void actionCancel() {
		state = CANCELED;
		setVisible(false);
	}
}

